home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / python-xdg / xdg / DesktopEntry.py < prev    next >
Encoding:
Python Source  |  2005-05-26  |  12.5 KB  |  384 lines

  1. """
  2. Complete implementation of the XDG Desktop Entry Specification Version 0.9.4
  3. http://standards.freedesktop.org/desktop-entry-spec/
  4.  
  5. Not supported:
  6. - Encoding: Legacy Mixed
  7. - Does not check exec parameters
  8. - Does not check URL's
  9. - Does not completly validate deprecated/kde items
  10. - Does not completly check categories
  11. """
  12.  
  13. from xdg.IniFile import *
  14. from xdg.BaseDirectory import *
  15. import os.path
  16.  
  17. class DesktopEntry(IniFile):
  18.     "Class to parse and validate DesktopEntries"
  19.  
  20.     defaultGroup = 'Desktop Entry'
  21.  
  22.     def __init__(self, filename=None):
  23.         self.content = dict()
  24.         if filename and os.path.exists(filename):
  25.             self.parse(filename)
  26.         elif filename:
  27.             self.new(filename)
  28.  
  29.     def __str__(self):
  30.         return self.getName()
  31.  
  32.     def __cmp__(self, other):
  33.         return cmp(self, other)
  34.  
  35.     def parse(self, file):
  36.         IniFile.parse(self, file, ["Desktop Entry", "KDE Desktop Entry"])
  37.  
  38.     # start standard keys
  39.     def getType(self):
  40.         return self.get('Type')
  41.     def getVersion(self):
  42.         return self.get('Version', type="numeric")
  43.     def getEncoding(self):
  44.         return self.get('Encoding')
  45.     def getName(self):
  46.         return self.get('Name', locale=True)
  47.     def getGenericName(self):
  48.         return self.get('GenericName', locale=True)
  49.     def getComment(self):
  50.         return self.get('Comment', locale=True)
  51.     def getNoDisplay(self):
  52.         return self.get('NoDisplay', type="boolean")
  53.     def getIcon(self):
  54.         return self.get('Icon', locale=True)
  55.     def getHidden(self):
  56.         return self.get('Hidden', type="boolean")
  57.     def getFilePattern(self):
  58.         return self.get('FilePattern', type="regex")
  59.     def getTryExec(self):
  60.         return self.get('TryExec')
  61.     def getExec(self):
  62.         return self.get('Exec')
  63.     def getPath(self):
  64.         return self.get('Path')
  65.     def getTerminal(self):
  66.         return self.get('Terminal', type="boolean")
  67.     def getSwallowTitle(self):
  68.         return self.get('SwallowTitle', locale=True)
  69.     def getSwallowExec(self):
  70.         return self.get('SwallowExec')
  71.     def getActions(self):
  72.         return self.get('Actions', list=True)
  73.     def getMimeType(self):
  74.         return self.get('MimeType', list=True, type="regex")
  75.     def getSortOrder(self):    
  76.         return self.get('SortOrder', list=True)
  77.     def getDev(self):
  78.         return self.get('Dev')
  79.     def getFSType(self):
  80.         return self.get('FSType')
  81.     def getMountPoint(self):
  82.         return self.get('MountPoint')
  83.     def getReadonly(self):
  84.         return self.get('ReadOnly', type="boolean")
  85.     def getUnmountIcon(self):
  86.         return self.get('UnmountIcon', locale=True)
  87.     def getURL(self):
  88.         return self.get('URL')
  89.     def getCategories(self):
  90.         return self.get('Categories', list=True)
  91.     def getOnlyShowIn(self):
  92.         return self.get('OnlyShowIn', list=True)
  93.     def getNotShowIn(self):
  94.         return self.get('NotShowIn', list=True)
  95.     def getStartupNotify(self):
  96.         return self.get('StartupNotify', type="boolean")
  97.     def getStartupWMClass(self):
  98.         return self.get('StartupWMClass')
  99.     # end standard keys
  100.  
  101.     # start kde keys
  102.     def getServiceTypes(self):
  103.         return self.get('ServiceTypes', list=True)
  104.     def getDocPath(self):
  105.         return self.get('DocPath')
  106.     def getKeywords(self):
  107.         return self.get('Keywords', list=True, locale=True)
  108.     def getInitialPreference(self):
  109.         return self.get('InitialPreference')
  110.     # end kde keys
  111.  
  112.     # start deprecated keys
  113.     def getMiniIcon(self):
  114.         return self.get('MiniIcon', locale=True)
  115.     def getTerminalOptions(self):
  116.         return self.get('TerminalOptions')
  117.     def getDefaultApp(self):
  118.         return self.get('DefaultApp')
  119.     def getProtocols(self):
  120.         return self.get('Protocols', list=True)
  121.     def getExtensions(self):
  122.         return self.get('Extensions', list=True)
  123.     def getBinaryPattern(self):
  124.         return self.get('BinaryPattern')
  125.     def getMapNotify(self):
  126.         return self.get('MapNotify')
  127.     # end deprecated keys
  128.  
  129.     # desktop entry edit stuff
  130.     def new(self, filename):
  131.         if os.path.splitext(filename)[1] == ".desktop":
  132.             type = "Application"
  133.         elif os.path.splitext(filename)[1] == ".directory":
  134.             type = "Directory"
  135.         self.content = dict()
  136.         self.addGroup(self.defaultGroup)
  137.         self.set("Encoding", "UTF-8")
  138.         self.set("Type", type)
  139.         self.filename = filename
  140.     # end desktop entry edit stuff
  141.  
  142.     # validation stuff
  143.     def checkExtras(self):
  144.         # header
  145.         if self.defaultGroup == "KDE Desktop Entry":
  146.             self.warnings.append('[KDE Desktop Entry]-Header is deprecated')
  147.  
  148.         # file extension
  149.         if self.fileExtension == ".kdelnk":
  150.             self.warnings.append("File extension .kdelnk is deprecated")
  151.         elif self.fileExtension != ".desktop" and self.fileExtension != ".directory":
  152.             self.warnings.append('Unknown File extension')
  153.  
  154.         # Type
  155.         try:
  156.             self.type = self.content[self.defaultGroup]["Type"]
  157.         except KeyError:
  158.             self.errors.append("Key 'Type' is missing")
  159.  
  160.         # Encoding
  161.         try:
  162.             self.encoding = self.content[self.defaultGroup]["Encoding"]
  163.         except KeyError:
  164.             self.errors.append("Key 'Encoding' is missing")
  165.  
  166.         # Version
  167.         try:
  168.             self.version = self.content[self.defaultGroup]["Version"]
  169.         except KeyError:
  170.             self.warnings.append("Key 'Version' is missing")
  171.  
  172.         # Name
  173.         try:
  174.             self.name = self.content[self.defaultGroup]["Name"]
  175.         except KeyError:
  176.             self.errors.append("Key 'Name' is missing")
  177.  
  178.     def checkGroup(self, group):
  179.         # check if group header is valid
  180.         if not (group == self.defaultGroup \
  181.         or re.match("^\Desktop Action [a-zA-Z]+\$", group) \
  182.         or (re.match("^\X-", group) and group.decode("utf-8", "ignore").encode("ascii", 'ignore') == group)):
  183.             self.errors.append("Invalid Group name: %s" % group)
  184.         else:
  185.             #OnlyShowIn and NotShowIn
  186.             if self.content[group].has_key("OnlyShowIn") and self.content[group].has_key("NotShowIn"):
  187.             self.errors.append("Group may either have OnlyShowIn or NotShowIn, but not both")
  188.  
  189.     def checkKey(self, key, value, group):
  190.         # standard keys        
  191.         if key == "Type":
  192.             if value == "ServiceType" or value == "Service":
  193.                 self.warnings.append("Type=%s is a KDE extension" % key)
  194.             elif value == "MimeType":
  195.                 self.warnings.append("Type=MimeType is deprecated")
  196.             elif not (value == "Application" or value == "Link" or value == "FSDevice" or value == "Directory"):
  197.                 self.errors.append("Value of key 'Type' must be Application, Link, FSDevice or Directory, but is '%s'" % value)
  198.  
  199.             if self.fileExtension == ".directory" and not value == "Directory":
  200.                 self.warnings.append("File extension is .directory, but Type is '%s'" % value)
  201.             elif self.fileExtension == ".desktop" and value == "Directory":
  202.                 self.warnings.append("Files with Type=Directory should have the extension .directory")
  203.  
  204.         elif key == "Version":
  205.             self.checkValue(key, value, type="number")
  206.  
  207.         elif key == "Encoding":
  208.             if value == "Legacy-Mixed":
  209.                 self.errors.append("Encoding=Legacy-Mixed is deprecated and not supported by this parser")
  210.             elif not value == "UTF-8":
  211.                 self.errors.append("Value of key 'Encoding' must be UTF-8")
  212.  
  213.         elif re.match("^Name"+xdg.Locale.regex+"$", key):
  214.             pass # locale string
  215.  
  216.         elif re.match("^GenericName"+xdg.Locale.regex+"$", key):
  217.             pass # locale string
  218.  
  219.         elif re.match("^Comment"+xdg.Locale.regex+"$", key):
  220.             pass # locale string
  221.  
  222.         elif key == "NoDisplay":
  223.             self.checkValue(key, value, type="boolean")
  224.  
  225.         elif key == "Hidden":
  226.             self.checkValue(key, value, type="boolean")
  227.  
  228.         elif key == "Terminal":
  229.             self.checkValue(key, value, type="boolean")
  230.             self.checkType(key, "Application")
  231.  
  232.         elif key == "TryExec":
  233.             self.checkValue(key, value)
  234.             self.checkType(key, "Application")
  235.  
  236.         elif key == "Exec":
  237.             self.checkValue(key, value)
  238.             self.checkType(key, "Application")
  239.  
  240.         elif key == "Path":
  241.             self.checkValue(key, value)
  242.             self.checkType(key, "Application")
  243.  
  244.         elif re.match("^Icon"+xdg.Locale.regex+"$", key):
  245.             self.checkValue(key, value)
  246.  
  247.         elif re.match("^SwallowTitle"+xdg.Locale.regex+"$", key):
  248.             self.checkType(key, "Application")
  249.  
  250.         elif key == "SwallowExec":
  251.             self.checkValue(key, value)
  252.             self.checkType(key, "Application")
  253.  
  254.         elif key == "FilePatterns":
  255.             self.checkValue(key, value, type="regex", list=True)
  256.             self.checkType(key, "Application")
  257.  
  258.         elif key == "Actions":
  259.             self.checkValue(key, value, list=True)
  260.             self.checkType(key, "Application")
  261.  
  262.         elif key == "MimeType":
  263.             self.checkValue(key, value, type="regex", list=True)
  264.             self.checkType(key, "Application")
  265.  
  266.         elif key == "Categories":
  267.             self.checkValue(key, value)
  268.             self.checkType(key, "Application")
  269.             self.checkCategorie(value)
  270.  
  271.         elif key == "OnlyShowIn":
  272.             self.checkValue(key, value, list=True)
  273.             self.checkOnlyShowIn(value)
  274.  
  275.         elif key == "NotShowIn":
  276.             self.checkValue(key, value, list=True)
  277.             self.checkOnlyShowIn(value)
  278.  
  279.         elif key == "StartupNotify":
  280.             self.checkValue(key, value, type="boolean")
  281.             self.checkType(key, "Application")
  282.  
  283.         elif key == "StartupWMClass":
  284.             self.checkType(key, "Application")
  285.  
  286.         elif key == "SortOrder":
  287.             self.checkValue(key, value, list=True)
  288.             self.checkType(key, "Directory")
  289.  
  290.         elif key == "URL":
  291.             self.checkValue(key, value)
  292.             self.checkType(key, "URL")
  293.  
  294.         elif key == "Dev":
  295.             self.checkValue(key, value)
  296.             self.checkType(key, "FSDevice")
  297.  
  298.         elif key == "FSType":
  299.             self.checkValue(key, value)
  300.             self.checkType(key, "FSDevice")
  301.  
  302.         elif key == "MountPoint":
  303.             self.checkValue(key, value)
  304.             self.checkType(key, "FSDevice")
  305.  
  306.         elif re.match("^UnmountIcon"+xdg.Locale.regex+"$", key):
  307.             self.checkValue(key, value)
  308.             self.checkType(key, "FSDevice")
  309.  
  310.         elif key == "ReadOnly":
  311.             self.checkValue(key, value, type="boolean")
  312.             self.checkType(key, "FSDevice")
  313.  
  314.         # kde extensions
  315.         elif key == "ServiceTypes":
  316.             self.checkValue(key, value, list=True)
  317.             self.warnings.append("Key '%s' is a KDE extension" % key)
  318.  
  319.         elif key == "DocPath":
  320.             self.checkValue(key, value)
  321.             self.warnings.append("Key '%s' is a KDE extension" % key)
  322.  
  323.         elif re.match("^Keywords"+xdg.Locale.regex+"$", key):
  324.             self.checkValue(key, value, list=True)
  325.             self.warnings.append("Key '%s' is a KDE extension" % key)
  326.  
  327.         elif key == "InitialPreference":
  328.             self.checkValue(key, value, type="number")
  329.             self.warnings.append("Key '%s' is a KDE extension" % key)
  330.  
  331.         # deprecated keys
  332.         elif re.match("^MiniIcon"+xdg.Locale.regex+"$", key):
  333.             self.checkValue(key, value)
  334.             self.warnings.append("Key '%s' is deprecated" % key)
  335.  
  336.         elif key == "TerminalOptions":
  337.             self.checkValue(key, value)
  338.             self.warnings.append("Key '%s' is deprecated" % key)
  339.  
  340.         elif key == "DefaultApp":
  341.             self.checkValue(key, value)
  342.             self.warnings.append("Key '%s' is deprecated" % key)
  343.  
  344.         elif key == "Protocols":
  345.             self.checkValue(key, value, list=True)
  346.             self.warnings.append("Key '%s' is deprecated" % key)
  347.  
  348.         elif key == "Extensions":
  349.             self.checkValue(key, value, list=True)
  350.             self.warnings.append("Key '%s' is deprecated" % key)
  351.  
  352.         elif key == "BinaryPattern":
  353.             self.checkValue(key, value)
  354.             self.warnings.append("Key '%s' is deprecated" % key)
  355.  
  356.         elif key == "MapNotify":
  357.             self.checkValue(key, value)
  358.             self.warnings.append("Key '%s' is deprecated" % key)
  359.  
  360.         # "X-" extensions
  361.         elif re.match("^X-[a-zA-Z0-9-]+", key):
  362.             pass
  363.  
  364.         else:
  365.             self.errors.append("Invalid key: %s" % key)
  366.  
  367.     def checkType(self, key, type):
  368.         if not self.type == type:
  369.             self.errors.append("Key '%s' only allowed in Type=%s" % (key, type))
  370.  
  371.     def checkOnlyShowIn(self, value):
  372.         values = self.getList(value)
  373.         valid = ["GNOME", "KDE", "ROX", "XFCE", "Old"]
  374.         for item in values:
  375.             if item not in valid:
  376.                 self.errors.append("'%s' is not a registered OnlyShowIn value" % item);
  377.  
  378.     def checkCategorie(self, value):
  379.         values = self.getList(value)
  380.         valid = ["Legacy","Core","Development","Building","Debugger","IDE","GUIDesigner","Profiling","RevisionControl","Translation","Office","Calendar","ContactManagement","Database","Dictionary","Chart","Email","Finance","FlowChart","PDA","ProjectManagement","Presentation","Spreadsheet","WordProcessor","Graphics","2DGraphics","VectorGraphics","RasterGraphics","3DGraphics","Scanning","OCR","Photograph","Viewer","Settings","DesktopSettings","HardwareSettings","PackageManager","Network","Dialup","InstantMessaging","IRCClient","FileTransfer","HamRadio","News","P2P","RemoteAccess","Telephony","WebBrowser","WebDevelopment","AudioVideo","Audio","Midi","Mixer","Sequencer","Tuner","Video","TV","AudioVideoEditing","Player","Recorder","DiscBurning","Game","ActionGame","AdventureGame","ArcadeGame","BoardGame","BlocksGame","CardGame","KidsGame","LogicGame","RolePlaying","Simulation","SportsGame","StrategyGame","Education","Art","Art","Contruction","Music","Languages","Science","Astronomy","Biology","Chemistry","Geology","Math","MedicalSoftware","Physics","Teaching","Amusement","Applet","Archiving","Electronics","Emulator","Engineering","FileManager","Shell","Screensaver","TerminalEmulator","TrayIcon","System","Filesystem","Monitor","Security","Utility","Accessibility","Calculator","Clock","TextEditor","KDE","GNOME","GTK","Qt","Motif","Java","ConsoleOnly"]
  381.         for item in values:
  382.             if item not in valid:
  383.                 self.errors.append("'%s' is not a registered Category" % item);
  384.